<?
/****************************************************************************
 * (c) 2003, 2004
 * 	Jonathan Haskins <jhaskins@killobyte.com>
 *	Felix C. Courtemanche <felix@wd1.net>
 *	Jez Hancock <jez.hancock@munk.nu>
 * <place copyright notice here for each person who contributes to this file>
 *
 * TERMS OF USAGE:
 * This file is part of Web://CP.
 *
 * Web://CP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 ****************************************************************************/

/* $Id: webcp-httpd-functions.inc.phps,v 1.14 2007/03/30 14:44:00 gyrbo Exp $ */

/*
 * File:	webcp-httpd-functions.inc.phps
 * Purpose:	isolate functions used by Web://CP Web Serving Daemon
 * Creation:	2003-01-25
 */

function http_daemon($socket, &$client) {

	/* check for http header double carriage return / line feed */
	if (empty($client['hlength'])) {
		if (($client['hlength'] = strpos($client['buffer'], "\r\n\r\n")) !== false) {
			$client['hlength'] += 4;

			/* According to what I've read in the HTTP RFC's (rfc1945, rfc2068, rfc2616)
			 * all client requests with a body, i.e. POSTs, are REQUIRED to have the
			 * Content-Length header. This would also apply for HTTP 1.1, despite
			 * chunking. So I guess we'll just make sure all the data arrived here. */
			if ($client['clength'] = stristr($client['buffer'], 'Content-Length:')) {
				$client['clength'] = substr($client['clength'], 0, strpos($client['clength'], "\r\n"));
				$client['clength'] = explode(':', $client['clength']);
				$client['clength'] = trim($client['clength'][1]);
			}

			if (empty($client['clength'])) {
				$client['clength'] = 0;
			}
		} else {
			/* we don't have a header length yet */
			return;
		}
	}

	/* If the full body content hasn't arrived yet, skip the next part. */
	if (strlen($client['buffer']) < ($client['hlength'] + $client['clength'])) {
		return;
	}

	/* this is where we parse the request headers */
	$request = parse_request($client['buffer']);

	/* request has been parsed, erase the buffer. */
	$client['buffer'] = '';
	unset($client['hlength']);
	unset($client['clength']);

	/* build response array based on the request array */
	$response = build_response($request);

	/* then send our response */
	send_response($socket, $client, $response, $request);

	/* close the connection if we're done */
	if ($response['connection_close']) {
		close_client($socket);
	}
}

function http_stop($signo='') {
/* shutdown the listening socket and close all client sockets. */

	global $cfg, $lsocket, $csocket, $echo;

	// close server's listening socket and disconnect all clients
	close_socket();

	/* if using SSL then shut down stunnel */
	if ($cfg['ssl']) {
		if (file_exists($cfg['httpsd_pid'])) {
			$tmp = trim(file_get_contents($cfg['httpsd_pid']));
 			if (!posix_kill($tmp, 15))
				webcp_log(1,0,'webcp-httpd',"stunnel could not be shut down.",0,$echo);
			else
				webcp_log(2,0,'webcp-httpd',"stunnel has been stopped.",0,$echo);
		}
	}

	// remove pidfile
	if (is_writable($cfg['httpd_pid']) && !unlink($cfg['httpd_pid'])) {
		webcp_log(1, 0, 'webcp-httpd', "Unable to remove $cfg[httpd_pid]", 0, $echo);
	}

	webcp_log(2,0,'webcp-httpd',"webcp-httpd daemon has been stopped.",0,$echo);
	exit;
}

function http_reload($signo='') {
/* on SIGHUP we reload the server configuration */

	global $cfg, $echo;

	if($cfg['httpd_prefork']) {
		// There isn't really any need to do anything on sighup...
		global $childpid;

		//forward sighup to all children
		foreach($childpid as $pid=>$ignore) {
			posix_kill($pid, 1);
		}
	} else {

		/* change back to script dir */
		chdir(dirname(__FILE__));

		/* reload configuration */
		include('../web/config.inc.php');

		/* reconnect to database */
		db_connect($cfg['dbhost'], $cfg['dbname'], $cfg['dbuser'], $cfg['dbpass']);
	}

	webcp_log(2, 0, 'webcp-httpd', "SIGHUP recieved, server was reloaded.", 0, $echo);
}

function child_reload($signo='') {
	global $cfg, $echo;

	if(false) {
		/* change back to script dir */
		chdir(dirname(__FILE__));

		/* reload configuration */
		include('../web/config.inc.php');

		//db_connect($cfg['dbhost'], $cfg['dbname'], $cfg['dbuser'], $cfg['dbpass']);
	}

	webcp_log(2, 0, 'webcp-httpd', "SIGHUP recieved, child was reloaded.", 0, $echo);
}

function parse_request(&$data) {
/******************************************************************
 * take the raw request data from a client and return something like:
 *
 * $request
 * (
 *	[method]		=> GET
 *	[protocol]		=> HTTP/1.1
 *	[file]			=> test.php
 *	[connection_close] 	=> false
 *	[get]
 *	(
 *		[key1]	=> val1
 *		[key2]	=> val2
 *	)
 * )
 *******************************************************************/

	GLOBAL $cfg;

	$data = explode("\r\n\r\n", $data, 2);

	$header = explode("\r\n", $data[0]);
	/* if using ssl grab the custom SSL_Remote_IP: header from stunnel.
	   To make sure it isn't faked by the client we only accept the first instance
	   of this header, directly on 'top'.
	*/
	if ($cfg['ssl'])
		$request['remote_ip'] = trim(array_shift($header));

	$request_line = explode(' ', $header[0]);
	unset($header[0]);

	/* make sure it's a valid HTTP request */
	if (!isset($request_line[2]) || strpos($request_line[2], "HTTP/") === false) {
		return false;
	}

	$request['method'] = $request_line[0];
	$request['protocol'] = $request_line[2];

	$request['cookie'] = array();
	$request['connection_close'] = false;

	/* Here we're going through the headers one by one
	 * looking for the few that we actually care about */
	foreach ($header as $val) {
		if (stristr($val, 'User-Agent:')) {
			/* pass user-agent string on to use
			 * with the $_SERVER variable */
			$val = substr($val, 11);
			$request['user_agent'] = trim($val);
		} elseif (stristr($val, 'Accept-Language:')) {
			/* pass accept-language string on to
			 * use with the $_SERVER variable */
			$val = substr($val, 16);
			$request['language'] = trim($val);
		} elseif (stristr($val, 'Cookie:')) {
			/* parse any cookies */
			$val = substr($val, 7);
			$request['cookie'] = parse_query(trim($val), '; ');
			continue;
		} elseif (stristr($val, 'Connection: close')) {
			/* see if they want us to close the connection */
			$request['connection_close'] = true;
			continue;
		} elseif (stristr($val, 'Content-Type:')) {
 			/* get content-type */
 			$val = substr($val, 13);
 			$request['content_type'] = trim($val);
 			continue;
		} elseif (stristr($val, 'If-Modified-Since:')) {
			/* parse any cookies */
			$val = substr($val, 18);
			$request['modified'] = strtotime(trim($val));
			continue;
		} elseif (stristr($val, 'Host:')) {
 			/* $_SERVER['HTTP_HOST'] */
 			list($val) = explode(':', substr($val, 5));
 			$request['host'] = trim($val);
 			continue;
		}
	}

	/* save requested URI for $_SERVER */
	$request['uri'] = $request_line[1];

	/* split any GET querries from the requested path */
	$path_query = explode('?', $request_line[1]);
	$request['file'] = $path_query[0];

	/* save query string for $_SERVER */
	$request['query_string'] = '';
	if (isset($path_query[1])) {
		$request['query_string'] = $path_query[1];
	}

	// initialize file, post, and get data arrays
	$request['get'] = array();
	$request['post'] = array();
	$request['files'] = array();

	/* determine if there are POST or GET queries, and parse them */
	if (isset($path_query[1]) && strpos($path_query[1], '=') !== false) {
		$request['get'] = parse_query($path_query[1]);
	}

	// check for included request body
 	if (isset($data[1]) && isset($request['content_type'])) {
 		if (strpos($request['content_type'], 'application/x-www-form-urlencoded') !== false) {
 			// normal form
 			$request['post'] = parse_query($data[1]);
 		} elseif (strpos($request['content_type'], 'multipart/form-data') !== false) {
 			// rfc 2388 covers multipart/form-data
 			$boundary = explode(';', $request['content_type']);
 			$boundary = explode('=', $boundary[1]);
 			$boundary = trim($boundary[1]);

 			parse_multi_part($data[1], $boundary, $request['post'], $request['files']);
 		} else {
			// keep as raw data (XML?)
			$request['raw_post'] = $data[1];
		}
	}

	return $request;
}

function build_response($request) {
/******************************************************************
 * build the array that we will base our response on:
 *
 * $response
 * (
 *	[status]		=> ok
 *	[protocol]		=> HTTP/1.1
 *	[translated_file]	=> /home/webcp/web/index.php
 *	[file_type]		=> php
 *	[connection_close]	=> false
 * )
 *******************************************************************/
	global $cfg;

	$response['connection_close'] = false;

	/* the http 1.1 protocol uses keep alive connections. We should
	 * close a socket only when it has timed out, or one of the
	 * following conditions is met. */
	if (!$request) {
		/* we recieved a request we don't understand */
		$response['connection_close'] = true;
	} elseif ($request['protocol'] !== 'HTTP/1.1') {
		/* they are using a http 1.0 or unknown protocol */
		$response['connection_close'] = true;
	} elseif ($request['connection_close']) {
		/* they asked us to close it with a Connection: close header */
		$response['connection_close'] = true;
	} elseif (!$cfg['keep_alive'] || $cfg['ssl']) {
		/* keep alive is turned off or ssl turned on in the configuration file */
		$response['connection_close'] = true;
	}

	/* if the request we got is invalid respond as such */
	if (!$request) {
		$response['protocol'] = 'HTTP/1.1';
		$response['status'] = 'malformed';
		return $response;
	}

	if ($request['protocol'] == 'HTTP/1.1') {
		/* If they're using HTTP version 1.1 then respond with 1.1 */
		$response['protocol'] = 'HTTP/1.1';
	} else {
		/* If they're using any other version drop to 1.0 compatibility */
		$response['protocol'] = 'HTTP/1.0';
	}

	/* extremely basic security check, remove any ../ from requested file
	 * so they can't ascend into forbidden directories */
	$response['translated_file'] = str_replace('../', '', $request['file']);

	/* remove leading/trailing slash(es) */
	$response['translated_file'] = trim($response['translated_file'], '/');

	/* append default path from config file */
	$response['translated_file'] = $cfg['basedir'].'/web/'.$response['translated_file'];

	/* load index.php for directories */
	if (is_readable($response['translated_file']) && is_dir($response['translated_file'])) {
		$response['translated_file'] = rtrim($response['translated_file'], '/').'/index.php';
	}

	/* Get a file's extension by grabbing the characters
	 * from after the last period to the last character. */
	$response['file_type'] = substr($response['translated_file'], (strrpos($response['translated_file'], '.') + 1));

	/* we're working with a lot of files that may change */
	clearstatcache();

	/* see if requested file exists and can be served */
	if (basename($response['translated_file']) == '--status') {
		$response['status'] = 'status';
	} elseif (!file_exists($response['translated_file'])) {
		$response['status'] = 'not found';
	} elseif (!is_readable($response['translated_file'])) {
		$response['status'] = 'forbidden';
	} elseif (!is_dir($response['translated_file'])) {
		// Set last modified
		$response['last_modified'] = filemtime($response['translated_file']);

		/* change to file's working directory */
		chdir(dirname($response['translated_file']));
		if ((isset($request['modified'])) && ($response['file_type'] != 'php') && ($request['modified'] >= $response['last_modified'])) {
			$response['status'] = 'notmodified';
		} else {
			$response['status'] = 'ok';
		}
	}

	return $response;
}

function build_headers($response, $send_headers = false, $replace = false) {
/* compile an appropriate list of headers to send to client */

	global $cfg, $http_status_map;

	/* Status line */
	if (!in_array($response['status'], array_keys($http_status_map))) {
		$response['status']='default';
	}
	$headers = $response['protocol'].' '.$http_status_map[$response['status']]."\r\n";

	/* Date line, Sat, 06 Sep 2014 23:50:08 GMT*/
	$headers .= 'Date: '.gmdate("D, d M Y H:i:s T")."\r\n";

	/* Server line */
	$headers .= "Server: webcp-httpd - web-cp $cfg[webcp]\r\n";
	$headers .= 'X-Powered-By: PHP/'.phpversion()."\r\n";

	/* Add content last modified and length */
	switch($response['status']) {
		case 'ok':
			$headers .= 'Last-Modified: ' . gmdate('D, d M Y H:i:s', $response['last_modified'])." GMT\r\n";
			$headers .= 'Content-length: '.$response['content_length']."\r\n";
			break;
		case 'notmodified':
			break;
		default:
			$headers .= 'Content-length: '.$response['content_length']."\r\n";
			break;
	}

	/* Connection close header */
	if ($response['connection_close'] AND $response['protocol'] == 'HTTP/1.1') {
		$headers .= "Connection: close\r\n";
	}

	/* MIME type */
	switch($response['status']) {
		case 'notmodified':
			break;
		case 'ok':
			$headers .= 'Content-type: '.mime_type($response['file_type'])."\r\n";
			break;
		default:
			$headers .= "Content-type: text/html\r\n";
			break;
	}

	/* include any headers sent by the subscript using send_header() */
	if ($send_headers) {
		foreach($send_headers as $val) {
			$headers .= $val;
		}
	}

	if ($replace) {
		$headers = explode("\r\n", $headers);
		foreach ($headers AS $val) {
			$header_name = explode(':', $val, 2);
			$header[strtolower($header_name[0])] = $val;
		}
		$headers = implode("\r\n", $header)."\r\n";
	}

	$headers .= "\r\n";

	return $headers;
}

function send_response($socket, $client, $response, $request) {
/* handle the client's request */

	global $cfg, $httpd_status, $echo;

	if ($response['status'] == 'ok' && $response['file_type'] == 'php') {

		/* if the requested resource is a php file. */
		$content = load_page($client, $response, $request, $header_redirect, $send_headers);

		/* re-reference our globals that were reset in load_page() */
		global $cfg, $httpd_status, $echo;

		/* If $header_redirect is true then the loaded page used send_header()
		 * to send a Location: header. We don't send the content, instead we
		 * only send a redirection header */
		if ($header_redirect) {
			$response['status'] = 'redirect';
			$response['connection_close'] = true;
			$content = 'redirect';
		}

		// add up the content-length
		$response['content_length'] = intval(strlen($content));
		$headers = build_headers($response, $send_headers);

		if (@socket_write($socket, $headers.$content) === false) {
			webcp_log(1,0,'webcp-httpd',"Failed to send to {$client['remote_ip']} because: ".socket_strerror(socket_last_error($socket)),0,$echo);
			close_client($socket);
		}
	} elseif ($response['status'] == 'ok') {
		/* if the requested resource is a normal file and can be returned */

		$response['content_length'] = filesize($response['translated_file']);
		$headers = build_headers($response);

		if (@socket_write($socket, $headers) === false) {
			webcp_log(1,0,'webcp-httpd',"Failed to send to {$client['remote_ip']} because: ".socket_strerror(socket_last_error($socket)),0,$echo);
			close_client($socket);
		} else {
			socket_write_file($socket, $response['translated_file'], $client);
		}
	} elseif ($response['status'] == 'forbidden') {
		/* if the requested resource isn't readable */

		$content = "<html><head>\r\n";
		$content .= "<title>403 Forbidden</title>\r\n";
		$content .= "</head><body>\r\n";
		$content .= "<h1>Forbidden</h1>\r\n";
		$content .= "<p>You do not have permission to access $request[file].</p>\r\n";
		$content .= "<p><i>$cfg[sysname]</i></p>\r\n";
		$content .= "</body></html>";

		$response['content_length'] = strlen($content);
		$headers = build_headers($response);

		if (@socket_write($socket, $headers.$content) === false) {
			webcp_log(0,0,'webcp-httpd',"Failed to send to {$client['remote_ip']} because: ".socket_strerror(socket_last_error($socket)),0,$echo);
			close_client($socket);
		}
	} elseif ($response['status'] == 'not found') {
		/* if the requested resource doesn't exist */

		$content = "<html><head>\r\n";
		$content .= "<title>404 Not Found</title>\r\n";
		$content .= "</head><body>\r\n";
		$content .= "<h1>Not Found</h1>\r\n";
		$content .= "<p>The requested URL $request[file] was not found.</p>\r\n";
		$content .= "<p><i>$cfg[sysname]</i></p>\r\n";
		$content .= "</body></html>";

		$response['content_length'] = strlen($content);
		$headers = build_headers($response);

		if (@socket_write($socket, $headers.$content) === false) {
			webcp_log(1,0,'webcp-httpd',"Failed to send to {$client['remote_ip']} because: ".socket_strerror(socket_last_error($socket)),0,$echo);
			close_client($socket);
		}
	} elseif ($response['status'] == 'status') {
		/* they requested server status */

		/* convert uptime into days, hours, minutes, and seconds */
		$uptime = get_microtime() - $httpd_status['started'];
		$uptime_str = '';
		if (($days = floor($uptime / 86400)) > 0) $uptime_str .= "$days days";
		if (($hours = floor(($uptime % 86400) / (3600))) > 0) $uptime_str .= " $hours hours";
		if (($mins = floor((($uptime % 86400) % 3600) / 60)) > 0) $uptime_str .= " $mins min";
		if (($secs = floor((($uptime % 86400) % 3600) % 60)) > 0) $uptime_str .= " $secs sec";

		/* convert bytes sent into terra, giga, mega, kilo, or bytes */
		$sent_str = convert_bytes($httpd_status['sent'], $metric).' '.$metric;

		// Count number of code lines in web-cp
		$numlines = 0;
		$dir = dir_list($cfg['basedir']."/server", "file");
		do {
			if (strstr(current($dir),".php") AND !strstr(current($dir),"phpmyadmin") AND !strstr(current($dir),"phppgadmin") AND !strstr(current($dir),"errors"))
				$numlines += count(file(current($dir)));
		} while (next($dir));

		$dir = dir_list($cfg['basedir']."/web", "file");
		do {
			if (strstr(current($dir),".php") AND !strstr(current($dir),"phpmyadmin") AND !strstr(current($dir),"phppgadmin") AND !strstr(current($dir),"errors"))
				$numlines += count(file(current($dir)));
		} while (next($dir));

		$content = "<html><head>\n";
		$content .= "<title>web-cp status</title>\n";
		$content .= "</head><body>\n";
		$content .= "<h1>web-cp status</h1>\n";
		$content .= "<b>Version:</b> $cfg[webcp]<br>\n";
		$content .= "<b>Lines of code:</b> $numlines<br>\n";
		$content .= "<b>SSL:</b> ".($cfg['ssl']?'Enabled':'Disabled')."<br>\n";
		$content .= "<b>Keep-Alive:</b> ".(($cfg['keep_alive'] && !$cfg['ssl'])?'Enabled':'Disabled')."<br>\n";
		$content .= '<b>'.(($cfg['httpd_prefork']) ? 'Worker ':'')."Started:</b> ".date("r", $httpd_status['started'])."<br>\n";
		$content .= '<b>'.(($cfg['httpd_prefork']) ? 'Worker ':'')."Uptime:</b> $uptime_str<br>\n";
		$content .= '<b>'.(($cfg['httpd_prefork']) ? 'Worker ':'')."Data Served:</b> $sent_str<br>\n";
		$content .= '<b>'.(($cfg['httpd_prefork']) ? 'Worker ':'')."Hits:</b> $httpd_status[hits]<br>\n";
		$content .= '<b>'.(($cfg['httpd_prefork']) ? 'Worker ':'')."Connections:</b> $httpd_status[connections]<br>\n";
		$content .= "<p><i>$cfg[sysname]</i></p>\n";
		$content .= "</body></html>";

		$response['content_length'] = strlen($content);
		$headers = build_headers($response);

		if (@socket_write($socket, $headers.$content) === false) {
			webcp_log(1,0,'webcp-httpd',"Failed to send to {$client['remote_ip']} because: ".socket_strerror(socket_last_error($socket)),0,$echo);
			close_client($socket);
		}
	} elseif ($response['status'] == 'notmodified') {
		/* if the requested resource hasn't been modified */

		$response['content_length'] = 0;
		$headers = build_headers($response);

		if (@socket_write($socket, $headers) === false) {
			webcp_log(0,0,'webcp-httpd',"Failed to send to {$client['remote_ip']} because: ".socket_strerror(socket_last_error($socket)),0,$echo);
			close_client($socket);
		}
	} else {
		/* if we had an invalid request */
		$content = "<html><head>\r\n";
		$content .= "<title>400 Bad Request</title>\r\n";
		$content .= "</head><body>\r\n";
		$content .= "<h1>Bad Request</h1>\r\n";
		$content .= "<p>You sent a malformed header. Goodbye.</p>\r\n";
		$content .= "<p><i>$cfg[sysname]</i></p>\r\n";
		$content .= "</body></html>";

		$response['content_length'] = strlen($content);
		$headers = build_headers($response);

		if (@socket_write($socket, $headers.$content) === false) {
			webcp_log(0,0,'webcp-httpd',"Failed to send to {$client['remote_ip']} because: ".socket_strerror(socket_last_error($socket)),0,$echo);
			close_client($socket);
		}
	}

	/* update internal statistics */
	$httpd_status['hits']++;
	$httpd_status['sent'] += strlen($headers) + $response['content_length'];

	// log this request:
	log_http_request($client, $response, $request);
}

function load_page($client, $response, $request, &$header_redirect, &$send_headers) {
/* Interpret a seperate php file and return it in a variable. */

	global $cfg, $echo;

	/* make sure this script won't crash the server */
	if ($cfg['validate_php']) {
		if (!validate($response['translated_file'])) {
			return "Error: Requested file failed webcp-httpd compatiblity validation.";
		}
	}

	/* copy globals, work around for not being able to use $GLOBALS directly due to recursion */
	//foreach ($GLOBALS AS $key => $val) {
	//	if ($key !== 'GLOBALS') {
	//		$CLI_GLOBALS[$key] = $val;
	//	}
	//}

	$_SERVER = array();
	$_REQUEST = array();

	/* pass HTTP_SERVER_VARS array to subscript */
	$_SERVER['HTTP_HOST'] = isset($request['host'])?$request['host']:$cfg['sysname'];
	$_SERVER['HTTP_USER_AGENT'] = (isset($request['user_agent']) ? $request['user_agent'] : '');
	$_SERVER['HTTP_ACCEPT_LANGUAGE'] = (isset($request['language']) ? $request['language'] : '');
	$_SERVER['SERVER_SOFTWARE'] = "web-cp $cfg[webcp] php/".phpversion();
	$_SERVER['SERVER_NAME'] = $cfg['sysname']/*$_SERVER['HTTP_HOST']*/;
	$_SERVER['SERVER_ADDR'] = $client['local_ip'];
	$_SERVER['SERVER_PORT'] = $cfg['port'];
	if ($cfg['ssl']) {
		$_SERVER['HTTPS'] = 'on';
		$_SERVER['REMOTE_ADDR'] = $request['remote_ip'];
	} else {
		$_SERVER['REMOTE_ADDR'] = $client['remote_ip'];
		$_SERVER['REMOTE_PORT'] = $client['remote_port'];
	}
	$_SERVER['SERVER_PROTOCOL'] = $response['protocol'];
	$_SERVER['DOCUMENT_ROOT'] = $cfg['basedir'].'/web';
	$_SERVER['SCRIPT_FILENAME'] = $response['translated_file'];
	$_SERVER['REQUEST_METHOD'] = $request['method'];
	$_SERVER['QUERY_STRING'] = $request['query_string'];
	$_SERVER['REQUEST_URI'] = $request['uri'];
	$_SERVER['SCRIPT_NAME'] = substr($response['translated_file'], strlen($cfg['basedir'].'/web'));
	$_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'];
	if(isset($request['raw_post']))
		$_SERVER['HTTP_RAW_POST_DATA'] = $request['raw_post'];
	$HTTP_SERVER_VARS = $_SERVER;

	/* pass cookie array to subscript */
	$_COOKIE = $request['cookie'];
	$HTTP_COOKIE_VARS = $_COOKIE;
	$_REQUEST = array_merge($_REQUEST, $_COOKIE);

	/* pass post array to subscript */
	$_POST = $request['post'];
	$HTTP_POST_VARS = $_POST;
	$_REQUEST = array_merge($_REQUEST, $_POST);

	/* pass get array to subscript */
	$_GET = $request['get'];
	$HTTP_GET_VARS = $_GET;
	$_REQUEST = array_merge($_REQUEST, $_GET);

	/* pass HTTP_ENV_VARS array to subscript */
	$HTTP_ENV_VARS = $_ENV;

	/* pass files array to subscript */
	$_FILES = $request['files'];
	$HTTP_POST_FILES = $_FILES;

	/* emulate register_globals turned on */
	if (ini_get('register_globals')) {
		extract($_SERVER);
		extract($_REQUEST);
		extract($_ENV);
		extract($_FILES);
	}

	/* set error reporting to the php.ini default */
	restore_error_handler();
	$default_error_level = get_cfg_var('error_reporting');
	$error_level = error_reporting($default_error_level);

	/* evaluate the requested script and cache it to a variable. */
	ob_start();
	$result = include($response['translated_file']);
	$page = ob_get_contents();
	ob_end_clean();

	/* restore error reporting */
	//set_error_handler('error_handler');
	error_reporting($error_level);

	/* See if there is anything returned from running the script. */
	if (strlen($result) > 1) {
		/* if so, print it out */
		$page .= $result;
	}

	/* remove temporary files that were uploaded by client */
	foreach($_FILES AS $val) {
		/* make sure that this script can still access the file */
		if (is_writable($val['tmp_name'])) {
			unlink($val['tmp_name']);
		}
	}

	/* return header info by reference */
	$header_redirect = (isset($GLOBALS['header_redirect']) ? $GLOBALS['header_redirect'] : false);
	unset($GLOBALS['header_redirect']);
	$send_headers = (isset($GLOBALS['send_headers']) ? $GLOBALS['send_headers'] : false);
	unset($GLOBALS['send_headers']);

	/* clear all our global variables */
	//foreach ($GLOBALS AS $key => $val) {
	//	if ($key !== 'GLOBALS') {
	//		if (is_array($GLOBALS[$key])) {
	//			$GLOBALS[$key] = array();
	//		}
	//		unset($GLOBALS[$key]);
	//	}
	//}

	/* restore all global variables to our pre load_page() state */
	//foreach ($CLI_GLOBALS AS $key => $val) {
	//	$GLOBALS[$key] = $val;
	//}

	return $page;
}

function socket_write_file($socket, $file, $client) {
/* send a file line by line. */

	global $echo;

	$fp = fopen($file, "rb");
	while(!feof($fp)) {
		if (@socket_write($socket, fread($fp, MAX_LENGTH)) === false) {
			webcp_log(0,0,'webcp-httpd',"Failed to send to $client[remote_ip] because: ".socket_strerror(socket_last_error($socket)),0,$echo);
			close_client($socket);
			break;
		}
	}
	fclose($fp);
}

function mime_type($file_type) {
/* return the correct mime type based on the file type. */

	switch($file_type) {
		case 'php':
		case 'html':
			return 'text/html';
		case 'jpg':
		case 'jpeg':
			return 'image/jpeg';
		case 'png':
			return 'image/png';
		case 'gif':
			return 'image/gif';
		default:
			return 'text/plain';
	}
}

function parse_query($query_line, $delimiter='&') {
/* take a query line from a POST, GET or COOKIE and return an
 * array holding each query name and value */

	if ($delimiter !== '&') {
		// Encode ampersands to keep them from being split on
		$query_line = str_replace('&', '%amp', $query_line);
		$query_line = str_replace($delimiter, '&', $query_line);
	}

	parse_str(trim($query_line), $result);

	if ($delimiter !== '&') {
		// Decode ampersands
		foreach ($result AS $key => $val) {
			$result[$key] = str_replace('%amp', '&', $val);
		}
	}

	return $result;
}

function parse_multi_part(&$body, $boundary, &$post, &$files) {
/* This function will parse posted multipart form data
 * into temporary files or post queries which will be
 * passed back by reference */

	// split the multiple parts into an array
	$body = preg_split("/(\r\n)?--$boundary(--)?(\r\n)?/", $body, -1, PREG_SPLIT_NO_EMPTY);

	// cycle through the parts and parse each one
	foreach($body AS $part) {

		$part = explode("\r\n\r\n", $part);
		if (count($part) < 2) {
			$part[1] = '';
		}

		// separate the part's headers into an array
		$part[0] = explode("\r\n", $part[0]);
		foreach($part[0] AS $val) {
				$arr = explode(':', $val, 2);
				if (count($arr) > 1) {
					$mime_header[strtolower($arr[0])] = trim($arr[1]);
				}
		}

		// separate all attributes of the content disposition into an array
		if (isset($mime_header['content-disposition'])) {
			$attribute = explode(';', $mime_header['content-disposition']);

			foreach ($attribute AS $val) {
				$arr = explode('=', trim($val));
				if (count($arr) > 1) {
					$cont_disp[strtolower($arr[0])] = trim($arr[1], '"');
				}
			}
		}

		// check and see if this part is a file
		if (!empty($cont_disp['filename'])) {
			// generate a temporary name and get the size
			$tmp_name = '/tmp/php'.uniqid(getmypid());
			$size = strlen($part[1]);

			// add it and it's associated data to the files array that we're going to need
			$files[$cont_disp['name']]['name'] = $cont_disp['filename'];
			$files[$cont_disp['name']]['tmp_name'] = $tmp_name;
			$files[$cont_disp['name']]['size'] = $size;
			if (isset($mime_header['content-type'])) {
				$files[$cont_disp['name']]['type'] = $mime_header['content-type'];
			}

			// save the uploaded file to the tmp directory
			// we will delete the file when execution of this
			// request is finished.
			if ($fp = fopen($tmp_name, 'w')) {
				fwrite($fp, $part[1], $size);
				fclose($fp);
			}
		// if it's not a file it must be a posted query
		} elseif (!empty($part[1])) {
			if (isset($cont_disp['name'])) {
				$post[$cont_disp['name']] = $part[1];
			}
		}
	}
}

function validate($file) {
/* This function will parse a php file line by line looking for incompatible
 * statements. I think this check will go faster if we load the whole file into
 * memory at once rather than reading it line by line. Since this will only be
 * done once per file the memory use is acceptible. */

	global $validated, $echo, $cfg;

	/* if this file was already tested return the results */
	if (isset($validated[$file]) ) {
		return $validated[$file];
	}

	$content = file($file);

	/* check for contraband */
	/* This needs to be much improved. Some regex/pcre guru should
	 * fix these to actually take all cases into account and not
	 * cause false positives, like we should ignore if within blocked
	 * comments */
	foreach ($content as $num => $line) {
		$num++;

		/* functions are declared more than once causing a fatal error. */
		if (preg_match("/(?!\/\/).*?function\s+.*?\(/i", $line)) {
			$validated[$file] = false;
			webcp_log(0,0,'webcp-httpd',"Error: $file has a function declaration on line ".$num,0,$echo);
			break;
		}
		/* require will kill the server if the file isn't found */
		if (preg_match("/(?!\/\/).*?require(_once)?[\s|\(]/i", $line)) {
			$validated[$file] = false;
			webcp_log(0,0,'webcp-httpd',"Error: $file has a require() on line ".$num,0,$echo);
			break;
		}
		/* exit kills the server */
		if (preg_match("/(?!\/\/).*?exit[\s|\(|;]/i", $line)) {
			$validated[$file] = false;
			webcp_log(0,0,'webcp-httpd',"Error: $file has an exit on line ".$num,0,$echo);
			break;
		}
		/* die kills the server */
		if (preg_match("/(?!\/\/).*?die[\s|\(|;]/i", $line)) {
			$validated[$file] = false;
			webcp_log(0,0,'webcp-httpd',"Error: $file has a die on line ".$num,0,$echo);
			break;
		}
		/* set_time_limit kills the server after x many seconds */
		if (preg_match("/(?!\/\/).*?set_time_limit[\s|\(]/i", $line)) {
			$validated[$file] = false;
			webcp_log(0,0,'webcp-httpd',"Error: $file has a set_time_limit() call on line ".$num,0,$echo);
			break;
		}

		/* see if they've included any other files and parse them. if they used a
		 * variable in the path we ignore it. we should catch at least some
		 * includes and chances are we'll find something somewhere down the line
		 * if the program isn't compatible. If not, oh well. Kaboom! */
		if (ereg("include(_once)?.*\((.+)\)", $line, $match)) {
			if (strpos($match[2], '$') === false) {
				if ($include = realpath(trim($match[2], "\"\'"))) {
					if (!validate($include)) {
						$validated[$file] = false;
						break;
					}
				}
			}
		}
	}

	/* if we made it this far without an error then the file is validated */
	if (!isset($validated[$file])) {
		$validated[$file] = true;
	}

	return $validated[$file];
}

function log_http_request($client, $response, $request){
	global $cfg, $http_status_map, $echo;
	/*
		Log this hit using the Common Log Format:
		Equivalent to LogFormat:
		LogFormat "%h %l %u %t \"%r\" %>s %b" common

		which puts out log lines like:
		127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326

		see: http://httpd.apache.org/docs/mod/mod_log_config.html#formats for details
	*/
	if (!isset($http_status_map[$response['status']])) {
		$response['status']='default';
	}
	$tmp=split(" ", $http_status_map[$response['status']]);
	$status_code=$tmp[0];

	// Leave the user field for now.  Could be done?
	$clf_log=	"$client[remote_ip] - - ";
	$clf_log.=	"[".gmdate("d/M/Y H:i:s T")."] ";
	$clf_log.=	"\"$request[method] $request[uri] $request[protocol]\" $status_code $response[content_length]\n";

	if( isset($echo) && $echo == 1 ) {
		print $clf_log."\r\n";
	}

	$fp=fopen($cfg["accesslog"], "a");

	if(!$fp){
		webcp_log(0,0,'webcp-httpd',"Non-Fatal Error: could not open $cfg[accesslog] for writing",0,$echo);
	} else {
		fwrite($fp, $clf_log);
		fclose($fp);
	}
	return 1;
}

function get_microtime() {
/* used to calculate timeouts, also useful for testing */

	return array_sum(explode(' ', microtime()));
}

function db_connect($dbhost, $dbname, $dbuser, $dbpass) {

        global $echo;

        if (!@mysql_connect($dbhost, $dbuser, $dbpass)) {
                webcp_log(0,0,'webcp-httpd',"Error:  Could not connect to database server",0,$echo);
                return false;
        }
        elseif (!@mysql_select_db($dbname)) {
                webcp_log(0,0,'webcp-httpd',"Error:  Could not select database",0,$echo);
                return false;
        }
}
?>
